package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Contém os cálculos de IQA para o parâmetro Fósforo.
 * 
 * @author Tiago
 */
public final class CalculoFosforoBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade do Fósforo (qi^w)
	 */
	private static final double PESO = 0.10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo dos exponensiais.
	 */
	private static final int ESCALA_EXP = 20;

	/**
	 * Define o primeiro intervalo. De 0.1 a menos que 0.4.
	 */
	public static final double[] PRIMEIRO_INTERVALO = { 0.1, 0.4 };

	/**
	 * Define o primeiro intervalo. De 0.4 a 3.
	 */
	public static final double[] SEGUNDO_INTERVALO = { 0.4, 3 };

	private CalculoFosforoBusiness() {
	}

	/**
	 * Utilizado para fazer o cálculo do Fósforo caso o valor informado é menor
	 * do que 0.1.
	 * 
	 * Cálculo realizado:
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 */
	private static BigDecimal menorPrimeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se valor >= 0.1 o mesmo está incorreto
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) >= 0) {
			throw new BusinessException();
		}

		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		// -3818,50533807828*B14^2 + 34,19928825622*B14 + 98,22953736655
		BigDecimal retorno = new BigDecimal("3818.50533807828")
				.multiply(valorExp2).negate() // -3818,50533807828*[valor]^2
				.add(new BigDecimal("34.19928825622").multiply(valor))
				// + 34,19928825622*[valor]
				.add(new BigDecimal("98.22953736655"));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo do Fósforo caso o valor informado é maior
	 * ou igual a 0.1 e menor a 0.4.
	 * 
	 * Cálculo realizado: 250,000000000004*[valor]^2 - 215,000000000001*[valor]
	 * + 83;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal primeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se valor < 0.1 ou valor >= 0.4 o mesmo está incorreto
		if ((valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) < 0)
				|| (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) >= 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		BigDecimal retorno = new BigDecimal("250.000000000004")
				.multiply(valorExp2) // 250,000000000004*[valor]^2
				.subtract(new BigDecimal("215.000000000001").multiply(valor))
				// - 215,000000000001*[valor]
				.add(new BigDecimal("83"));
		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo do Fósforo caso o valor informado é maior
	 * ou igual a 0.4 e menor ou igual a 3.
	 * 
	 * Cálculo realizado: 1,7769523572*[valor]^4 - 14,93199539525*[valor]^3 +
	 * 48,76939791778*[valor]^2 - 77,86535904492*[valor] + 60,88864660144;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal segundoIntervalo(BigDecimal valor)
			throws BusinessException {
		// se valor < 0.4 ou > 3 o mesmo está incorreto
		if ((valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[0])) < 0)
				|| (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp4 = new BigDecimal(Math.pow(valor.doubleValue(), 4))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// 1,7769523572*[valor]^4 - 14,93199539525*[valor]^3 +
		// 48,76939791778*[valor]^2 - 77,86535904492*[valor] + 60,88864660144;
		// SE([valor]>3; 5)))))))
		BigDecimal retorno = new BigDecimal("1.7769523572").multiply(valorExp4) // 1,7769523572*[valor]^4
				.subtract(new BigDecimal("14.93199539525").multiply(valorExp3))
				// - 14,93199539525*[valor]^3
				.add(new BigDecimal("48.76939791778").multiply(valorExp2))
				// + 48,76939791778*[valor]^2
				.subtract(new BigDecimal("77.86535904492").multiply(valor))
				// - 77,86535904492*[valor]
				.add(new BigDecimal("60.88864660144"));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Calcula o IQA do Fósforo informado. Cálculo realizado: Se [valor] < que 0
	 * Lança exceção Se [valor] < 0.1
	 * 
	 * @see {@link CalculoFosforoBusiness#menorPrimeiroIntervalo(double)} Se
	 *      [valor] < 0.4
	 * @see {@link CalculoFosforoBusiness#primeiroIntervalo(double)} Se [valor]
	 *      <= 3
	 * @see {@link CalculoFosforoBusiness#segundoIntervalo(double)} Se [valor] >
	 *      3 IQA = 5;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido (Ex: menor que
	 *             0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {

		// =SE([valor]="";"NM";SE([valor]<0;"ERR";
		if (valor.compareTo(BigDecimal.ZERO) < 0) {
			throw new BusinessException();
		}

		// SE([valor]<0,1; -3818,50533807828*[valor]^2 + 34,19928825622*[valor]
		// +
		// 98,22953736655;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) < 0) {
			return menorPrimeiroIntervalo(valor);
		}

		// SE([valor]<0,4; 250,000000000004*[valor]^2 - 215,000000000001*[valor]
		// + 83;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) < 0) {
			return primeiroIntervalo(valor);
		}

		// SE([valor]<3; 1,7769523572*[valor]^4 - 14,93199539525*[valor]^3 +
		// 48,76939791778*[valor]^2 - 77,86535904492*[valor] + 60,88864660144;
		if (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) <= 0) {
			return segundoIntervalo(valor);
		}

		// se o valor for maior que que o fim do ultimo intervalo retorna 5
		final int retornoMaiorQueUltimIntervalo = 5;
		return new BigDecimal(retornoMaiorQueUltimIntervalo);

	}

	/**
	 * Calcula a média de qualidade (qi^w).
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
